package com.spring.sd;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

/**
 * BeanFactory 的实现
 */

@Slf4j
public class BeanFactoryApplication {


    public static void main(String[] args) {

        // 目前可以知道，BeanFactory 不会：
        //       主动调用 BeanFactory 后置处理器；
        //       主动添加 Bean 后置处理器；
        //       主动初始化单例对象；
        //       解析 ${} 和 #{}

        // 1、需要使用到 BeanFactory 的一个实现类： DefaultListableBeanFactory。有了 Bean 工厂，还需要定义 Bean，之后再把定义的 Bean 注册到工厂即可。
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 2、尝试将 com.spring.sd.BeanFactoryApplication.Config 添加到 Bean 工厂中
        // bean 的定义（class，scope，初始化，销毁）
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config", beanDefinition);

        // 现在 Bean 工厂中 有且仅有一个 名为 config 的 BeanDefinition
        //Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
        //System.out.println(beanFactory.getBean(Config.class));

        // 根据对 @Configuration 和 @Bean 两个注解的认识可知，Bean 工厂中应该还存在 bean1 和 bean2，那为什么现在没有呢？
        // 很明显是现在的 BeanFactory 缺少了解析 @Configuration 和 @Bean 两个注解的能力。
        // 3、给 BeanFactory 添加一些常用的后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        //Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);

        // internalAutowiredAnnotationProcessor解析@Autowired注解
        // internalCommonAnnotationProcessor解析@Resource注解
        // 4、使用后置处理器 BeanFactoryPostProcessor 补充了一些 Bean 的定义
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(i -> i.postProcessBeanFactory(beanFactory));
        //Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);

        // bean2为null，bean2 没有成功被注入到 bean1 中。
        //System.out.println(beanFactory.getBean(Bean1.class).getBean2());

        // 在先前添加到 BeanFactory 中的后置处理器里，有名为 internalAutowiredAnnotationProcessor 和 internalCommonAnnotationProcessor 的两个后置处理器。
        // 前者用于解析 @Autowired 注解，后者用于解析 @Resource 注解，它们都有一个共同的类型 BeanPostProcessor，因此可以
        // 5、要添加Bean后置处理器，对 Bean 的生命周期的各个阶段提供拓展，例如 @AutoWired @Resource...
        // 建立 BeanPostProcessor 和 BeanFactory 的关系后，bean2 被成功注入到 bean1 中了。
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
        //System.out.println(beanFactory.getBean(Bean1.class).getBean2());

        // 当需要使用 Bean 时，Bean 才会被创建，即按需加载。beanFactory.preInstantiateSingletons();预先就初始化好单例对象
        // 6、预先初始化单例对象（完成依赖注入和初始化流程）
        beanFactory.preInstantiateSingletons();
        System.out.println("---------------------------------------------");
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
        System.out.println(beanFactory.getBean(Bean1.class).getBean2());

    }












    @Configuration
    @Slf4j
    static class Config {

        public Config() {
            log.info("Config 构造方法调用");
        }

        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    @Slf4j
    static class Bean1 {
        public Bean1() {
            log.info("构造 Bean1()");
        }

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {
            return bean2;
        }
    }

    @Slf4j
    static class Bean2 {
        public Bean2() {
            log.info("构造 Bean2()");
        }
    }
}
